package cc.shacocloud.mirage.restful;

import cc.shacocloud.mirage.restful.bind.support.AbstractArgumentValidateResolver;
import cc.shacocloud.mirage.restful.exception.HttpMediaTypeNotSupportedException;
import cc.shacocloud.mirage.restful.exception.InvalidMediaTypeException;
import cc.shacocloud.mirage.restful.http.GenericHttpMessageConverter;
import cc.shacocloud.mirage.restful.http.HttpMessageConverter;
import cc.shacocloud.mirage.restful.http.MediaType;
import cc.shacocloud.mirage.utils.MethodParameter;
import cc.shacocloud.mirage.utils.ResolvableType;
import cc.shacocloud.mirage.utils.collection.SelfSortList;
import cc.shacocloud.mirage.utils.comparator.AnnotationOrderComparator;
import io.vertx.core.Future;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpMethod;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;

import java.lang.reflect.Type;
import java.util.*;

/**
 * 基类，用于通过从与请求的主体读取解决方法参数值 HttpMessageConverters
 */
@Slf4j
public abstract class AbstractMessageConverterMethodArgumentResolver extends AbstractArgumentValidateResolver implements HandleMethodArgumentResolver {
    
    private static final Set<HttpMethod> SUPPORTED_METHODS = new HashSet<>(Arrays.asList(HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH));
    
    protected final List<HttpMessageConverter<?>> messageConverters = new SelfSortList<>(AnnotationOrderComparator.INSTANCE::getOrder);
    
    protected final List<MediaType> allSupportedMediaTypes;
    
    protected AbstractMessageConverterMethodArgumentResolver(List<HttpMessageConverter<?>> messageConverters) {
        this.messageConverters.addAll(messageConverters);
        this.allSupportedMediaTypes = getAllSupportedMediaTypes(messageConverters);
    }
    
    /**
     * 通过{@link MediaType#sortBySpecificity(List)}返回所有提供的按特异性排序*的消息转换器支持的媒体类型。
     */
    private static List<MediaType> getAllSupportedMediaTypes(List<HttpMessageConverter<?>> messageConverters) {
        Set<MediaType> allSupportedMediaTypes = new LinkedHashSet<>();
        for (HttpMessageConverter<?> messageConverter : messageConverters) {
            allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
        }
        List<MediaType> result = new ArrayList<>(allSupportedMediaTypes);
        MediaType.sortBySpecificity(result);
        return Collections.unmodifiableList(result);
    }
    
    /**
     * 通过从给定的 VertXRoutingContext 中读取来创建预期参数类型的方法参数值
     *
     * @param <T>        要创建的参数值的预期类型
     * @param request    当前请求对象
     * @param parameter  方法参数描述符
     * @param targetType 目标类型，不一定与方法参数类型相同
     * @return 创建的方法参数值
     */
    @SuppressWarnings("unchecked")
    protected <T> Future<?> readWithMessageConverters(@NotNull HttpRequest request,
                                                      MethodParameter parameter,
                                                      Type targetType) {
        MediaType contentType;
        boolean noContentType = false;
        try {
            contentType = request.headers().getContentType();
            
            if (log.isDebugEnabled()) {
                log.debug("从请求中找到预设的 'Content-Type' : " + contentType);
            }
            
        } catch (InvalidMediaTypeException ex) {
            return Future.failedFuture(new HttpMediaTypeNotSupportedException(ex.getMessage()));
        }
        
        if (contentType == null) {
            noContentType = true;
            contentType = MediaType.APPLICATION_OCTET_STREAM;
            
            if (log.isDebugEnabled()) {
                log.debug("请求中未预设 'Content-Type' 使用默认值: " + contentType);
            }
        }
        
        Class<?> contextClass = parameter.getContainingClass();
        Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
        if (targetClass == null) {
            ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
            targetClass = (Class<T>) resolvableType.resolve();
        }
        
        HttpMethod httpMethod = request.method();
        
        Future<?> bodyFuture = null;
        
        // 循环所有的消息转换器
        for (HttpMessageConverter<?> converter : this.messageConverters) {
            // 如果是 基于 GenericHttpMessageConverter 的实现 则使用 GenericHttpMessageConverter来判断
            GenericHttpMessageConverter<?> genericConverter = (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
            if (genericConverter != null ? genericConverter.canRead(parameter, targetType, contextClass, contentType) :
                    (targetClass != null && converter.canRead(parameter, targetClass, contentType))) {
                
                if (log.isDebugEnabled()) {
                    log.debug("匹配到参数解析处理类：" + (genericConverter != null ? genericConverter : converter));
                }
                
                // 读取数据
                bodyFuture = (genericConverter != null ? genericConverter.read(parameter, targetType, contextClass, request) :
                        ((HttpMessageConverter<T>) converter).read(parameter, targetClass, request));
                
                if (bodyFuture != null) break;
            }
        }
        
        // 如果消息主体为空
        if (bodyFuture == null) {
            
            if (log.isDebugEnabled()) {
                log.debug("方法参数 '" + parameter.getParameterName() + "' 未匹配到参数解析处理类！");
            }
            
            Buffer requestBody = request.getBody();
            if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod)
                    || (noContentType && (requestBody == null || requestBody.length() == 0))) {
                return Future.succeededFuture();
            }
            return Future.failedFuture(new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes));
        }
        
        return bodyFuture;
    }
    
}
